﻿using System;
using System.Reflection;
using WindowServices.Common;
using WindowServices.Interface;
using WindowServices.Server.Interface;
using WindowServices.Service.Properties;

namespace WindowServices.Service
{
    internal sealed class ServiceHelper : MarshalByRefObject, IService, IDisposable
    {
        private readonly ServiceEntityEx _serviceEntity;
        private SecuritySwitchThread _securitySwitchThread;
        private IService _service;

        private ServiceHelper(ServiceEntityEx serviceEntity)
        {
            _serviceEntity = serviceEntity;
        }

        public AppDomain CurrentDomain
        {
            get { return AppDomain.CurrentDomain; }
        }

        public string ServiceName
        {
            get { return _serviceEntity.Name; }
        }

        public string PlugBasePath
        {
            get { return _serviceEntity.BasePath; }
        }

        #region IDisposable Members

        public void Dispose()
        {
            CurrentDomain.DoCallBack(() =>
                                         {
                                             if (_securitySwitchThread != null)
                                             {
                                                 _securitySwitchThread.Dispose();
                                             }
                                         }
                );
        }

        #endregion

        #region IService Members

        public void Initialize(ServiceEntity serviceEntity)
        {
            try
            {
                CurrentDomain.DoCallBack(() =>
                                             {
                                                 var paras = new string[2];
                                                 paras[0] = serviceEntity.Type.Split(',')[0].Trim();
                                                 paras[1] = serviceEntity.Type.Replace(paras[0] + ",", "").Trim();

                                                 _service = CreateService(paras);
                                                 if (_service == null)
                                                 {
                                                     throw new Exception(Resources.CreateServiceObjectError);
                                                 }

                                                 _service.Initialize(serviceEntity);
                                                 _securitySwitchThread = new SecuritySwitchThread(_service.Start,
                                                                                                  _serviceEntity);
                                             });
            }
            catch (Exception exception)
            {

                LogHelper.WriteEntityServiceError(string.Format(Resources.InitServiceError, serviceEntity.Name, exception), EventType.CreateServiceError);
            }
        }

        public void Start()
        {
            try
            {
                CurrentDomain.DoCallBack(() =>
                                    _securitySwitchThread.Start());
            }
            catch (Exception exception)
            {

                LogHelper.WriteEntityServiceError(string.Format(Resources.InitServiceError, _serviceEntity.Name, exception), EventType.CreateServiceError);
            }

        }

        public void Stop()
        {
            try
            {
                CurrentDomain.DoCallBack(() => _service.Stop());
            }
            catch (Exception exception)
            {

                LogHelper.WriteEntityServiceError(string.Format(Resources.StopServiceError, _serviceEntity.Name, exception), EventType.CreateServiceError);
            }
        }

        public void Pause()
        {
            try
            {
                CurrentDomain.DoCallBack(() => _service.Pause());
            }
            catch (Exception exception)
            {
                LogHelper.WriteEntityServiceError(string.Format(Resources.PauseServerError, _serviceEntity.Name, exception), EventType.CreateServiceError);
            }
        }

        #endregion

        public static ServiceHelper CreateServiceHelper(ServiceEntityEx serviceEntity)
        {
            AppDomain domain = null;
            try
            {
                var appsetup = new AppDomainSetup
                                   {
                                       PrivateBinPath =
                                           string.Format("{0}Plug\\{1}\\{2};", AppDomain.CurrentDomain.BaseDirectory,
                                                         serviceEntity.Id, serviceEntity.Version)
                                   };
                //appsetup.PrivateBinPath = AppDomain.CurrentDomain.BaseDirectory;// string.Format("Plug\\{0}\\{1}", serviceEntity.ID, serviceEntity.Version);
                // appsetup.PrivateBinPathProbe
                domain = AppDomain.CreateDomain(serviceEntity.Key, null, appsetup);
                //domain.AppendPrivatePath(string.Format("Plug\\{0}\\{1}", serviceEntity.ID, serviceEntity.Version));
                domain.UnhandledException += DomainUnhandledException;

                //domain.DoCallBack(new CrossAppDomainDelegate(loadassemable));
                const BindingFlags binding =
                    BindingFlags.CreateInstance | BindingFlags.NonPublic | BindingFlags.Instance;
                Type serviceType = typeof(ServiceHelper);
                var ob = domain.CreateInstance(serviceType.Assembly.FullName,
                                                        serviceType.FullName, false, binding, null,
                                                        new object[] { serviceEntity }, null, null);
                return (ServiceHelper)(ob.Unwrap());
            }
            catch (Exception ee)
            {
                LogHelper.WriteEntityServiceError(
                    string.Format(Resources.CreatePlugServiceError, ee, serviceEntity.Name, serviceEntity.Id,
                                  serviceEntity.Version), EventType.CreateServiceError);
                if (domain != null)
                {
                    AppDomain.Unload(domain);
                }

                return null;
            }
        }

        private static void DomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            LogHelper.WriteEntityServiceError(
                string.Format(Resources.PlugServiceException, e.IsTerminating, e.ExceptionObject),
                EventType.RunServiceError);
        }

// ReSharper disable once UnusedMember.Local
        private static void LoadAssemable()
        {
            Assembly.LoadFile(string.Format("{0}WindowServices.Common.dll", AppDomain.CurrentDomain.RelativeSearchPath));
        }

        private static IService CreateService(string[] strTypes)
        {
            return Assembly.Load(strTypes[1]).CreateInstance(strTypes[0]) as IService;
        }

        public void SetServiceState(ServiceState state)
        {
            CurrentDomain.DoCallBack(() => { _serviceEntity.State = state; });
        }

        public ServiceState GetServiceState()
        {
            return _serviceEntity.State;
        }
    }
}